#include <string.h>
#include <stdlib.h>
#include "args.h"
#include "kw.h"
#include "lexer.h"
#include "statement.h"

#define KEY_WIDTH 10

#define KW_POOL_SIZE      1024
#define STRING_POOL_SIZE  102400

static struct kw pool_data[KW_POOL_SIZE];

static  char string_pool[STRING_POOL_SIZE];
static int next_string = 0;

struct kw *pool_head = NULL;
struct kw *pool_tail= NULL;


void pool_free(struct kw *kw)
{
  kw->sib = NULL;
  if (pool_tail != NULL)
    pool_tail->sib = kw;
  else
    pool_head = kw;
  pool_tail = kw;
}

struct kw *pool_alloc(void)
{
  struct kw *kw = pool_head;
  if (pool_head != NULL)
    pool_head = pool_head->sib;
  if (pool_head == NULL) 
    pool_tail = NULL;
  return kw;
}

void pool_init(void)
{
  int i;
  for (i = 0; i < KW_POOL_SIZE; i++) {
    pool_free(&pool_data[i]);
  }
}

static char *alloc_string(const char *buf, int size)
{
  char *ptr = string_pool + next_string;
  if (size < STRING_POOL_SIZE - next_string){
    memcpy(ptr, buf, size);
    ptr[size] = 0;
    next_string = next_string + size + 1;
    return ptr;
  } else
    return NULL;
}


static struct kw *kw_tree = NULL;

static uint32_t flag_from_templ(const char *buf, seg_t s)
{
  uint32_t flag = 0;
  if (buf[start_seg(s)] == '<' && buf[end_seg(s) - 1] == '>')
    flag |= KW_FLAG_INPUT;
  return flag;
}
 
static struct kw *find_kw_son(struct kw *next, const char *buf, seg_t s)
{
  uint32_t flag = flag_from_templ(buf, s);
  if (next == NULL)
    next = kw_tree;
  else 
    next = next->son;
  while (next != NULL) {
    if (flag & KW_FLAG_INPUT) {
      if (next->flag & KW_FLAG_INPUT)
        break;
    } else {
      if (len_of_seg(s) == next->size && memcmp(next->word, buf + start_seg(s), next->size) == 0)
        break;
    }
    next=next->sib;
  }
  return next;
}

static void find_and_insert(struct kw *last, struct kw *kw)
{
  while (last != NULL) {
    if (last->sib == NULL) {
      last->sib = kw;
      break;
    }
    last = last->sib;
  }
}

static void insert_son_kw(struct kw *last, struct kw *kw)
{
  struct kw **son;
  if (last == NULL)
    son = &kw_tree;
  else 
    son = &last->son;
  if ((*son) == NULL)
    *son = kw;
  else
    find_and_insert(*son, kw);
}


static struct kw * kw_new(struct token *token, const char *buf, seg_t s, cmd_func_t func)
{
  struct kw *kw = pool_alloc();
  if (kw == NULL)
    return NULL;
  kw->flag = flag_from_templ(buf, s);
  kw->sib = NULL;
  kw->word = alloc_string(buf + start_seg(s), len_of_seg(s));
  kw->size = len_of_seg(s);
  if (token_size(token) == 0)
    kw->callback = func;
  else
    kw->callback = NULL;
  kw->sib = NULL;
  kw->son = NULL;
  return kw;
}

static void add_tree(struct token *token, const char *buf, cmd_func_t func)
{
  struct kw *last = NULL;
  do {
    seg_t s = token_pop_front(token);
    if (!valid_seg(s))
      break;
    struct kw *lookup = find_kw_son(last, buf, s);
    if (lookup == NULL) {
      lookup=kw_new(token, buf, s, func);
      if (lookup == NULL) {
        printf ("pool exausted\n");
        break;
      }
      insert_son_kw(last, lookup);
    }
    last = lookup;
  } while(1);
}

static void show_tree(struct kw *kw, int i)
{
  if (kw == NULL){
    return;
  }
  printf ("%*s%s\n", i, "", kw->word);
  show_tree(kw->son, i + 8);
  show_tree(kw->sib, i);
}

struct kw *kw_sib(struct kw *kw)
{
  return kw == NULL ? NULL : kw->sib;
}

struct kw * kw_son(struct kw *kw)
{
  return kw == NULL ? NULL : kw->son;
}

void kw_set_sib(struct kw *kw, struct kw *sib)
{
	if (kw)
		kw->sib = sib;
}

int kw_is_input(struct kw *kw)
{
   return kw != NULL && (kw->flag & KW_FLAG_INPUT) != 0;
}

int kw_executable(struct kw *kw)
{
	return (kw != NULL && kw->callback != NULL);
}

int kw_match(struct kw *kw, const char *s, int len)
{
  if (kw == NULL)
		return -1;
	if (kw->size >= len)
		return memcmp(kw->word, s, len);
	else
		return -1;
}

#define KEY_TABLE_WIDTH 21

static void key_print(const char *buf, int size)
{
  int i;
  if (size > KEY_TABLE_WIDTH)
    size = KEY_TABLE_WIDTH - 1;
  for (i = 0; i < size; i++)
    printf("%c", buf[i]);
  while (size < KEY_TABLE_WIDTH) {
    printf (" ");
    size++;
  }
  fflush(stdout);
}

static int sib_help(struct kw *kw, int offset)
{
  struct kw *lookup = kw;
	while (lookup != NULL) {
    key_print(lookup->word, lookup->size);
    if (((offset + 1) % 4) == 0)
      printf("\n");
    offset++;
		lookup = kw_sib(lookup);
	}
  return offset;
}

#define ENTER "<cr>"

static int son_help(struct kw *kw, int offset)
{
	struct kw *s = kw_son(kw);
	offset = sib_help(s, offset);
	if (kw_executable(kw)) {
    key_print(ENTER, strlen(ENTER));
    if (((offset + 1) % 4) == 0)
      printf("\n");
    offset++;
  }
  return offset;
}

void kw_show_sib(struct kw *kw)
{
  sib_help(kw, 0);
  printf("\n");
}

void kw_show_son(struct kw *kw)
{
  son_help(kw, 0);  
  printf("\n");
} 

void kw_execute(struct kw *kw, const char *buf, struct seg_list *args)
{
	set_args(buf, args);
	kw->callback();
	unset_args();
}


void init_kw(void)
{
  pool_init();
  do {
    struct token token;
    struct kw_statement *s = statement_pop();
    if (s == NULL)
      break;
    if (tokenize(&token, s->templ, strlen(s->templ)) != 0)
      break;
    add_tree(&token, s->templ, s->func);
  } while(1);
  lexer_set_root(kw_tree);
}

BOLT(show_tree, "cmd tree")
{
  show_tree(kw_tree, 0);
}


